1 // Written in the D programming language.
2 
3 /**
4 This is a submodule of $(MREF std, math).
5 
6 It contains hardware support for floating point numbers.
7 
8 Copyright: Copyright The D Language Foundation 2000 - 2011.
9 License:   $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
10 Authors:   $(HTTP digitalmars.com, Walter Bright), Don Clugston,
11            Conversion of CEPHES math library to D by Iain Buclaw and David Nadlinger
12 Source: $(PHOBOSSRC std/math/hardware.d)
13  */
14 
15 module std.math.hardware;
16 
17 static import core.stdc.fenv;
18 
19 version (X86)       version = X86_Any;
20 version (X86_64)    version = X86_Any;
21 version (PPC)       version = PPC_Any;
22 version (PPC64)     version = PPC_Any;
23 version (MIPS32)    version = MIPS_Any;
24 version (MIPS64)    version = MIPS_Any;
25 version (AArch64)   version = ARM_Any;
26 version (ARM)       version = ARM_Any;
27 version (S390)      version = IBMZ_Any;
28 version (SPARC)     version = SPARC_Any;
29 version (SPARC64)   version = SPARC_Any;
30 version (SystemZ)   version = IBMZ_Any;
31 version (RISCV32)   version = RISCV_Any;
32 version (RISCV64)   version = RISCV_Any;
33 
34 version (D_InlineAsm_X86)    version = InlineAsm_X86_Any;
35 version (D_InlineAsm_X86_64) version = InlineAsm_X86_Any;
36 
37 version (X86_64) version = StaticallyHaveSSE;
38 version (X86) version (OSX) version = StaticallyHaveSSE;
39 
40 version (StaticallyHaveSSE)
41 {
42     private enum bool haveSSE = true;
43 }
44 else version (X86)
45 {
46     version(CustomRuntimeTest)
47     {
48         private immutable bool haveSSE = false;
49     }
50     else
51     {
52         static import core.cpuid;
53         private alias haveSSE = core.cpuid.sse;
54     }
55 }
56 
57 version (D_SoftFloat)
58 {
59     // Some soft float implementations may support IEEE floating flags.
60     // The implementation here supports hardware flags only and is so currently
61     // only available for supported targets.
62 }
63 else version (X86_Any)   version = IeeeFlagsSupport;
64 else version (PPC_Any)   version = IeeeFlagsSupport;
65 else version (RISCV_Any) version = IeeeFlagsSupport;
66 else version (MIPS_Any)  version = IeeeFlagsSupport;
67 else version (ARM_Any)   version = IeeeFlagsSupport;
68 
69 // Struct FloatingPointControl is only available if hardware FP units are available.
70 version (D_HardFloat)
71 {
72     // FloatingPointControl.clearExceptions() depends on version IeeeFlagsSupport
73     version (IeeeFlagsSupport) version = FloatingPointControlSupport;
74 }
75 
76 version (IeeeFlagsSupport)
77 {
78 
79 /** IEEE exception status flags ('sticky bits')
80 
81  These flags indicate that an exceptional floating-point condition has occurred.
82  They indicate that a NaN or an infinity has been generated, that a result
83  is inexact, or that a signalling NaN has been encountered. If floating-point
84  exceptions are enabled (unmasked), a hardware exception will be generated
85  instead of setting these flags.
86  */
87 struct IeeeFlags
88 {
89 nothrow @nogc:
90 
91 private:
92     // The x87 FPU status register is 16 bits.
93     // The Pentium SSE2 status register is 32 bits.
94     // The ARM and PowerPC FPSCR is a 32-bit register.
95     // The SPARC FSR is a 32bit register (64 bits for SPARC 7 & 8, but high bits are uninteresting).
96     // The RISC-V (32 & 64 bit) fcsr is 32-bit register.
97     uint flags;
98 
99     version (CRuntime_Microsoft)
100     {
101         // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
102         // Applies to both x87 status word (16 bits) and SSE2 status word(32 bits).
103         enum : int
104         {
105             INEXACT_MASK   = 0x20,
106             UNDERFLOW_MASK = 0x10,
107             OVERFLOW_MASK  = 0x08,
108             DIVBYZERO_MASK = 0x04,
109             INVALID_MASK   = 0x01,
110 
111             EXCEPTIONS_MASK = 0b11_1111
112         }
113         // Don't bother about subnormals, they are not supported on most CPUs.
114         //  SUBNORMAL_MASK = 0x02;
115     }
116     else
117     {
118         enum : int
119         {
120             INEXACT_MASK    = core.stdc.fenv.FE_INEXACT,
121             UNDERFLOW_MASK  = core.stdc.fenv.FE_UNDERFLOW,
122             OVERFLOW_MASK   = core.stdc.fenv.FE_OVERFLOW,
123             DIVBYZERO_MASK  = core.stdc.fenv.FE_DIVBYZERO,
124             INVALID_MASK    = core.stdc.fenv.FE_INVALID,
125             EXCEPTIONS_MASK = core.stdc.fenv.FE_ALL_EXCEPT,
126         }
127     }
128 
129     static uint getIeeeFlags() @trusted pure
130     {
131         version (InlineAsm_X86_Any)
132         {
133             ushort sw;
134             asm pure nothrow @nogc { fstsw sw; }
135 
136             // OR the result with the SSE2 status register (MXCSR).
137             if (haveSSE)
138             {
139                 uint mxcsr;
140                 asm pure nothrow @nogc { stmxcsr mxcsr; }
141                 return (sw | mxcsr) & EXCEPTIONS_MASK;
142             }
143             else return sw & EXCEPTIONS_MASK;
144         }
145         else version (SPARC)
146         {
147             /*
148                int retval;
149                asm pure nothrow @nogc { st %fsr, retval; }
150                return retval;
151             */
152             assert(0, "Not yet supported");
153         }
154         else version (ARM)
155         {
156             assert(false, "Not yet supported.");
157         }
158         else version (RISCV_Any)
159         {
160             mixin(`
161             uint result = void;
162             asm pure nothrow @nogc
163             {
164                 "frflags %0" : "=r" (result);
165             }
166             return result;
167             `);
168         }
169         else
170             assert(0, "Not yet supported");
171     }
172 
173     static void resetIeeeFlags() @trusted
174     {
175         version (InlineAsm_X86_Any)
176         {
177             asm nothrow @nogc
178             {
179                 fnclex;
180             }
181 
182             // Also clear exception flags in MXCSR, SSE's control register.
183             if (haveSSE)
184             {
185                 uint mxcsr;
186                 asm nothrow @nogc { stmxcsr mxcsr; }
187                 mxcsr &= ~EXCEPTIONS_MASK;
188                 asm nothrow @nogc { ldmxcsr mxcsr; }
189             }
190         }
191         else version (RISCV_Any)
192         {
193             mixin(`
194             uint newValues = 0x0;
195             asm pure nothrow @nogc
196             {
197                 "fsflags %0" : : "r" (newValues);
198             }
199             `);
200         }
201         else
202         {
203             /* SPARC:
204               int tmpval;
205               asm pure nothrow @nogc { st %fsr, tmpval; }
206               tmpval &=0xFFFF_FC00;
207               asm pure nothrow @nogc { ld tmpval, %fsr; }
208             */
209            assert(0, "Not yet supported");
210         }
211     }
212 
213 public:
214     /**
215      * The result cannot be represented exactly, so rounding occurred.
216      * Example: `x = sin(0.1);`
217      */
218     @property bool inexact() @safe const { return (flags & INEXACT_MASK) != 0; }
219 
220     /**
221      * A zero was generated by underflow
222      * Example: `x = real.min*real.epsilon/2;`
223      */
224     @property bool underflow() @safe const { return (flags & UNDERFLOW_MASK) != 0; }
225 
226     /**
227      * An infinity was generated by overflow
228      * Example: `x = real.max*2;`
229      */
230     @property bool overflow() @safe const { return (flags & OVERFLOW_MASK) != 0; }
231 
232     /**
233      * An infinity was generated by division by zero
234      * Example: `x = 3/0.0;`
235      */
236     @property bool divByZero() @safe const { return (flags & DIVBYZERO_MASK) != 0; }
237 
238     /**
239      * A machine NaN was generated.
240      * Example: `x = real.infinity * 0.0;`
241      */
242     @property bool invalid() @safe const { return (flags & INVALID_MASK) != 0; }
243 }
244 
245 ///
246 version (StdDdoc)
247 @safe unittest
248 {
249     import std.math.traits : isNaN;
250 
251     static void func() {
252         int a = 10 * 10;
253     }
254     real a = 3.5;
255     // Set all the flags to zero
256     resetIeeeFlags();
257     assert(!ieeeFlags.divByZero);
258     // Perform a division by zero.
259     a /= 0.0L;
260     assert(a == real.infinity);
261     assert(ieeeFlags.divByZero);
262     // Create a NaN
263     a *= 0.0L;
264     assert(ieeeFlags.invalid);
265     assert(isNaN(a));
266 
267     // Check that calling func() has no effect on the
268     // status flags.
269     IeeeFlags f = ieeeFlags;
270     func();
271     assert(ieeeFlags == f);
272 }
273 
274 @safe unittest
275 {
276     import std.math.traits : isNaN;
277 
278     static void func() {
279         int a = 10 * 10;
280     }
281     real a = 3.5;
282     // Set all the flags to zero
283     resetIeeeFlags();
284     assert(!ieeeFlags.divByZero);
285     // Perform a division by zero.
286     a = forceDivOp(a, 0.0L);
287     assert(a == real.infinity);
288     assert(ieeeFlags.divByZero);
289     // Create a NaN
290     a = forceMulOp(a, 0.0L);
291     assert(ieeeFlags.invalid);
292     assert(isNaN(a));
293 
294     // Check that calling func() has no effect on the
295     // status flags.
296     IeeeFlags f = ieeeFlags;
297     func();
298     assert(ieeeFlags == f);
299 }
300 
301 @safe unittest
302 {
303     import std.meta : AliasSeq;
304 
305     static struct Test
306     {
307         void delegate() @trusted action;
308         bool function() @trusted ieeeCheck;
309     }
310 
311     static foreach (T; AliasSeq!(float, double, real))
312     {{
313         T x; // Needs to be here to avoid `call without side effects` warning.
314         auto tests = [
315             Test(
316                 () { x = forceAddOp!T(1, 0.1L); },
317                 () => ieeeFlags.inexact
318             ),
319             Test(
320                 () { x = forceDivOp!T(T.min_normal, T.max); },
321                 () => ieeeFlags.underflow
322             ),
323             Test(
324                 () { x = forceAddOp!T(T.max, T.max); },
325                 () => ieeeFlags.overflow
326             ),
327             Test(
328                 () { x = forceDivOp!T(1, 0); },
329                 () => ieeeFlags.divByZero
330             ),
331             Test(
332                 () { x = forceDivOp!T(0, 0); },
333                 () => ieeeFlags.invalid
334             )
335         ];
336         foreach (test; tests)
337         {
338             resetIeeeFlags();
339             assert(!test.ieeeCheck());
340             test.action();
341             assert(test.ieeeCheck());
342         }
343     }}
344 }
345 
346 /// Set all of the floating-point status flags to false.
347 void resetIeeeFlags() @trusted nothrow @nogc
348 {
349     IeeeFlags.resetIeeeFlags();
350 }
351 
352 ///
353 version (StdDdoc)
354 @safe unittest
355 {
356     resetIeeeFlags();
357     real a = 3.5;
358     a /= 0.0L;
359     assert(a == real.infinity);
360     assert(ieeeFlags.divByZero);
361 
362     resetIeeeFlags();
363     assert(!ieeeFlags.divByZero);
364 }
365 
366 @safe unittest
367 {
368     resetIeeeFlags();
369     real a = 3.5;
370     a = forceDivOp(a, 0.0L);
371     assert(a == real.infinity);
372     assert(ieeeFlags.divByZero);
373 
374     resetIeeeFlags();
375     assert(!ieeeFlags.divByZero);
376 }
377 
378 /// Returns: snapshot of the current state of the floating-point status flags
379 @property IeeeFlags ieeeFlags() @trusted pure nothrow @nogc
380 {
381    return IeeeFlags(IeeeFlags.getIeeeFlags());
382 }
383 
384 ///
385 version (StdDdoc)
386 @safe nothrow unittest
387 {
388     import std.math.traits : isNaN;
389 
390     resetIeeeFlags();
391     real a = 3.5;
392 
393     a /= 0.0L;
394     assert(a == real.infinity);
395     assert(ieeeFlags.divByZero);
396 
397     a *= 0.0L;
398     assert(isNaN(a));
399     assert(ieeeFlags.invalid);
400 }
401 
402 @safe nothrow unittest
403 {
404     import std.math.traits : isNaN;
405 
406     resetIeeeFlags();
407     real a = 3.5;
408 
409     a = forceDivOp(a, 0.0L);
410     assert(a == real.infinity);
411     assert(ieeeFlags.divByZero);
412 
413     a = forceMulOp(a, 0.0L);
414     assert(isNaN(a));
415     assert(ieeeFlags.invalid);
416 }
417 
418 } // IeeeFlagsSupport
419 
420 
421 version (FloatingPointControlSupport)
422 {
423 
424 /** Control the Floating point hardware
425 
426   Change the IEEE754 floating-point rounding mode and the floating-point
427   hardware exceptions.
428 
429   By default, the rounding mode is roundToNearest and all hardware exceptions
430   are disabled. For most applications, debugging is easier if the $(I division
431   by zero), $(I overflow), and $(I invalid operation) exceptions are enabled.
432   These three are combined into a $(I severeExceptions) value for convenience.
433   Note in particular that if $(I invalidException) is enabled, a hardware trap
434   will be generated whenever an uninitialized floating-point variable is used.
435 
436   All changes are temporary. The previous state is restored at the
437   end of the scope.
438 
439 
440 Example:
441 ----
442 {
443     FloatingPointControl fpctrl;
444 
445     // Enable hardware exceptions for division by zero, overflow to infinity,
446     // invalid operations, and uninitialized floating-point variables.
447     fpctrl.enableExceptions(FloatingPointControl.severeExceptions);
448 
449     // This will generate a hardware exception, if x is a
450     // default-initialized floating point variable:
451     real x; // Add `= 0` or even `= real.nan` to not throw the exception.
452     real y = x * 3.0;
453 
454     // The exception is only thrown for default-uninitialized NaN-s.
455     // NaN-s with other payload are valid:
456     real z = y * real.nan; // ok
457 
458     // The set hardware exceptions and rounding modes will be disabled when
459     // leaving this scope.
460 }
461 ----
462 
463  */
464 struct FloatingPointControl
465 {
466 nothrow @nogc:
467 
468     alias RoundingMode = uint; ///
469 
470     version (StdDdoc)
471     {
472         enum : RoundingMode
473         {
474             /** IEEE rounding modes.
475              * The default mode is roundToNearest.
476              *
477              *  roundingMask = A mask of all rounding modes.
478              */
479             roundToNearest,
480             roundDown, /// ditto
481             roundUp, /// ditto
482             roundToZero, /// ditto
483             roundingMask, /// ditto
484         }
485     }
486     else version (CRuntime_Microsoft)
487     {
488         // Microsoft uses hardware-incompatible custom constants in fenv.h (core.stdc.fenv).
489         enum : RoundingMode
490         {
491             roundToNearest = 0x0000,
492             roundDown      = 0x0400,
493             roundUp        = 0x0800,
494             roundToZero    = 0x0C00,
495             roundingMask   = roundToNearest | roundDown
496                              | roundUp | roundToZero,
497         }
498     }
499     else
500     {
501         enum : RoundingMode
502         {
503             roundToNearest = core.stdc.fenv.FE_TONEAREST,
504             roundDown      = core.stdc.fenv.FE_DOWNWARD,
505             roundUp        = core.stdc.fenv.FE_UPWARD,
506             roundToZero    = core.stdc.fenv.FE_TOWARDZERO,
507             roundingMask   = roundToNearest | roundDown
508                              | roundUp | roundToZero,
509         }
510     }
511 
512     /***
513      * Change the floating-point hardware rounding mode
514      *
515      * Changing the rounding mode in the middle of a function can interfere
516      * with optimizations of floating point expressions, as the optimizer assumes
517      * that the rounding mode does not change.
518      * It is best to change the rounding mode only at the
519      * beginning of the function, and keep it until the function returns.
520      * It is also best to add the line:
521      * ---
522      * pragma(inline, false);
523      * ---
524      * as the first line of the function so it will not get inlined.
525      * Params:
526      *    newMode = the new rounding mode
527      */
528     @property void rounding(RoundingMode newMode) @trusted
529     {
530         initialize();
531         setControlState((getControlState() & (-1 - roundingMask)) | (newMode & roundingMask));
532     }
533 
534     /// Returns: the currently active rounding mode
535     @property static RoundingMode rounding() @trusted pure
536     {
537         return cast(RoundingMode)(getControlState() & roundingMask);
538     }
539 
540     alias ExceptionMask = uint; ///
541 
542     version (StdDdoc)
543     {
544         enum : ExceptionMask
545         {
546             /** IEEE hardware exceptions.
547              *  By default, all exceptions are masked (disabled).
548              *
549              *  severeExceptions = The overflow, division by zero, and invalid
550              *  exceptions.
551              */
552             subnormalException,
553             inexactException, /// ditto
554             underflowException, /// ditto
555             overflowException, /// ditto
556             divByZeroException, /// ditto
557             invalidException, /// ditto
558             severeExceptions, /// ditto
559             allExceptions, /// ditto
560         }
561     }
562     else version (ARM_Any)
563     {
564         enum : ExceptionMask
565         {
566             subnormalException    = 0x8000,
567             inexactException      = 0x1000,
568             underflowException    = 0x0800,
569             overflowException     = 0x0400,
570             divByZeroException    = 0x0200,
571             invalidException      = 0x0100,
572             severeExceptions   = overflowException | divByZeroException
573                                  | invalidException,
574             allExceptions      = severeExceptions | underflowException
575                                  | inexactException | subnormalException,
576         }
577     }
578     else version (PPC_Any)
579     {
580         enum : ExceptionMask
581         {
582             inexactException      = 0x0008,
583             divByZeroException    = 0x0010,
584             underflowException    = 0x0020,
585             overflowException     = 0x0040,
586             invalidException      = 0x0080,
587             severeExceptions   = overflowException | divByZeroException
588                                  | invalidException,
589             allExceptions      = severeExceptions | underflowException
590                                  | inexactException,
591         }
592     }
593     else version (RISCV_Any)
594     {
595         enum : ExceptionMask
596         {
597             inexactException      = 0x01,
598             divByZeroException    = 0x08,
599             underflowException    = 0x02,
600             overflowException     = 0x04,
601             invalidException      = 0x10,
602             severeExceptions   = overflowException | divByZeroException
603                                  | invalidException,
604             allExceptions      = severeExceptions | underflowException
605                                  | inexactException,
606         }
607     }
608     else version (HPPA)
609     {
610         enum : ExceptionMask
611         {
612             inexactException      = 0x01,
613             underflowException    = 0x02,
614             overflowException     = 0x04,
615             divByZeroException    = 0x08,
616             invalidException      = 0x10,
617             severeExceptions   = overflowException | divByZeroException
618                                  | invalidException,
619             allExceptions      = severeExceptions | underflowException
620                                  | inexactException,
621         }
622     }
623     else version (MIPS_Any)
624     {
625         enum : ExceptionMask
626         {
627             inexactException      = 0x0080,
628             divByZeroException    = 0x0400,
629             overflowException     = 0x0200,
630             underflowException    = 0x0100,
631             invalidException      = 0x0800,
632             severeExceptions   = overflowException | divByZeroException
633                                  | invalidException,
634             allExceptions      = severeExceptions | underflowException
635                                  | inexactException,
636         }
637     }
638     else version (SPARC_Any)
639     {
640         enum : ExceptionMask
641         {
642             inexactException      = 0x0800000,
643             divByZeroException    = 0x1000000,
644             overflowException     = 0x4000000,
645             underflowException    = 0x2000000,
646             invalidException      = 0x8000000,
647             severeExceptions   = overflowException | divByZeroException
648                                  | invalidException,
649             allExceptions      = severeExceptions | underflowException
650                                  | inexactException,
651         }
652     }
653     else version (IBMZ_Any)
654     {
655         enum : ExceptionMask
656         {
657             inexactException      = 0x08000000,
658             divByZeroException    = 0x40000000,
659             overflowException     = 0x20000000,
660             underflowException    = 0x10000000,
661             invalidException      = 0x80000000,
662             severeExceptions   = overflowException | divByZeroException
663                                  | invalidException,
664             allExceptions      = severeExceptions | underflowException
665                                  | inexactException,
666         }
667     }
668     else version (X86_Any)
669     {
670         enum : ExceptionMask
671         {
672             inexactException      = 0x20,
673             underflowException    = 0x10,
674             overflowException     = 0x08,
675             divByZeroException    = 0x04,
676             subnormalException    = 0x02,
677             invalidException      = 0x01,
678             severeExceptions   = overflowException | divByZeroException
679                                  | invalidException,
680             allExceptions      = severeExceptions | underflowException
681                                  | inexactException | subnormalException,
682         }
683     }
684     else
685         static assert(false, "Not implemented for this architecture");
686 
687     version (ARM_Any)
688     {
689         static bool hasExceptionTraps_impl() @safe
690         {
691             auto oldState = getControlState();
692             // If exceptions are not supported, we set the bit but read it back as zero
693             // https://sourceware.org/ml/libc-ports/2012-06/msg00091.html
694             setControlState(oldState | divByZeroException);
695             immutable result = (getControlState() & allExceptions) != 0;
696             setControlState(oldState);
697             return result;
698         }
699     }
700 
701     /// Returns: true if the current FPU supports exception trapping
702     @property static bool hasExceptionTraps() @safe pure
703     {
704         version (X86_Any)
705             return true;
706         else version (PPC_Any)
707             return true;
708         else version (MIPS_Any)
709             return true;
710         else version (ARM_Any)
711         {
712             // The hasExceptionTraps_impl function is basically pure,
713             // as it restores all global state
714             auto fptr = ( () @trusted => cast(bool function() @safe
715                 pure nothrow @nogc)&hasExceptionTraps_impl)();
716             return fptr();
717         }
718         else
719             assert(0, "Not yet supported");
720     }
721 
722     /// Enable (unmask) specific hardware exceptions. Multiple exceptions may be ORed together.
723     void enableExceptions(ExceptionMask exceptions) @trusted
724     {
725         assert(hasExceptionTraps);
726         initialize();
727         version (X86_Any)
728             setControlState(getControlState() & ~(exceptions & allExceptions));
729         else
730             setControlState(getControlState() | (exceptions & allExceptions));
731     }
732 
733     /// Disable (mask) specific hardware exceptions. Multiple exceptions may be ORed together.
734     void disableExceptions(ExceptionMask exceptions) @trusted
735     {
736         assert(hasExceptionTraps);
737         initialize();
738         version (X86_Any)
739             setControlState(getControlState() | (exceptions & allExceptions));
740         else
741             setControlState(getControlState() & ~(exceptions & allExceptions));
742     }
743 
744     /// Returns: the exceptions which are currently enabled (unmasked)
745     @property static ExceptionMask enabledExceptions() @trusted pure
746     {
747         assert(hasExceptionTraps);
748         version (X86_Any)
749             return (getControlState() & allExceptions) ^ allExceptions;
750         else
751             return (getControlState() & allExceptions);
752     }
753 
754     ///  Clear all pending exceptions, then restore the original exception state and rounding mode.
755     ~this() @trusted
756     {
757         clearExceptions();
758         if (initialized)
759             setControlState(savedState);
760     }
761 
762 private:
763     ControlState savedState;
764 
765     bool initialized = false;
766 
767     version (ARM_Any)
768     {
769         alias ControlState = uint;
770     }
771     else version (HPPA)
772     {
773         alias ControlState = uint;
774     }
775     else version (PPC_Any)
776     {
777         alias ControlState = uint;
778     }
779     else version (RISCV_Any)
780     {
781         alias ControlState = uint;
782     }
783     else version (MIPS_Any)
784     {
785         alias ControlState = uint;
786     }
787     else version (SPARC_Any)
788     {
789         alias ControlState = ulong;
790     }
791     else version (IBMZ_Any)
792     {
793         alias ControlState = uint;
794     }
795     else version (X86_Any)
796     {
797         alias ControlState = ushort;
798     }
799     else
800         static assert(false, "Not implemented for this architecture");
801 
802     void initialize() @safe
803     {
804         // BUG: This works around the absence of this() constructors.
805         if (initialized) return;
806         clearExceptions();
807         savedState = getControlState();
808         initialized = true;
809     }
810 
811     // Clear all pending exceptions
812     static void clearExceptions() @safe
813     {
814         version (IeeeFlagsSupport)
815             resetIeeeFlags();
816         else
817             static assert(false, "Not implemented for this architecture");
818     }
819 
820     // Read from the control register
821     package(std.math) static ControlState getControlState() @trusted pure
822     {
823         version (D_InlineAsm_X86)
824         {
825             short cont;
826             asm pure nothrow @nogc
827             {
828                 xor EAX, EAX;
829                 fstcw cont;
830             }
831             return cont;
832         }
833         else version (D_InlineAsm_X86_64)
834         {
835             short cont;
836             asm pure nothrow @nogc
837             {
838                 xor RAX, RAX;
839                 fstcw cont;
840             }
841             return cont;
842         }
843         else version (RISCV_Any)
844         {
845             mixin(`
846             ControlState cont;
847             asm pure nothrow @nogc
848             {
849                 "frcsr %0" : "=r" (cont);
850             }
851             return cont;
852             `);
853         }
854         else
855             assert(0, "Not yet supported");
856     }
857 
858     // Set the control register
859     package(std.math) static void setControlState(ControlState newState) @trusted
860     {
861         version (InlineAsm_X86_Any)
862         {
863             asm nothrow @nogc
864             {
865                 fclex;
866                 fldcw newState;
867             }
868 
869             // Also update MXCSR, SSE's control register.
870             if (haveSSE)
871             {
872                 uint mxcsr;
873                 asm nothrow @nogc { stmxcsr mxcsr; }
874 
875                 /* In the FPU control register, rounding mode is in bits 10 and
876                 11. In MXCSR it's in bits 13 and 14. */
877                 mxcsr &= ~(roundingMask << 3);             // delete old rounding mode
878                 mxcsr |= (newState & roundingMask) << 3;   // write new rounding mode
879 
880                 /* In the FPU control register, masks are bits 0 through 5.
881                 In MXCSR they're 7 through 12. */
882                 mxcsr &= ~(allExceptions << 7);            // delete old masks
883                 mxcsr |= (newState & allExceptions) << 7;  // write new exception masks
884 
885                 asm nothrow @nogc { ldmxcsr mxcsr; }
886             }
887         }
888         else version (RISCV_Any)
889         {
890             mixin(`
891             asm pure nothrow @nogc
892             {
893                 "fscsr %0" : : "r" (newState);
894             }
895             `);
896         }
897         else
898             assert(0, "Not yet supported");
899     }
900 }
901 
902 ///
903 @safe unittest
904 {
905     import std.math.rounding : lrint;
906 
907     FloatingPointControl fpctrl;
908 
909     fpctrl.rounding = FloatingPointControl.roundDown;
910     assert(lrint(1.5) == 1.0);
911 
912     fpctrl.rounding = FloatingPointControl.roundUp;
913     assert(lrint(1.4) == 2.0);
914 
915     fpctrl.rounding = FloatingPointControl.roundToNearest;
916     assert(lrint(1.5) == 2.0);
917 }
918 
919 @safe unittest
920 {
921     void ensureDefaults()
922     {
923         assert(FloatingPointControl.rounding
924                == FloatingPointControl.roundToNearest);
925         if (FloatingPointControl.hasExceptionTraps)
926             assert(FloatingPointControl.enabledExceptions == 0);
927     }
928 
929     {
930         FloatingPointControl ctrl;
931     }
932     ensureDefaults();
933 
934     {
935         FloatingPointControl ctrl;
936         ctrl.rounding = FloatingPointControl.roundDown;
937         assert(FloatingPointControl.rounding == FloatingPointControl.roundDown);
938     }
939     ensureDefaults();
940 
941     if (FloatingPointControl.hasExceptionTraps)
942     {
943         FloatingPointControl ctrl;
944         ctrl.enableExceptions(FloatingPointControl.divByZeroException
945                               | FloatingPointControl.overflowException);
946         assert(ctrl.enabledExceptions ==
947                (FloatingPointControl.divByZeroException
948                 | FloatingPointControl.overflowException));
949 
950         ctrl.rounding = FloatingPointControl.roundUp;
951         assert(FloatingPointControl.rounding == FloatingPointControl.roundUp);
952     }
953     ensureDefaults();
954 }
955 
956 @safe unittest // rounding
957 {
958     import std.meta : AliasSeq;
959 
960     static T addRound(T)(uint rm)
961     {
962         pragma(inline, false);
963         FloatingPointControl fpctrl;
964         fpctrl.rounding = rm;
965         T x = 1;
966         x = forceAddOp(x, 0.1L);
967         return x;
968     }
969 
970     static T subRound(T)(uint rm)
971     {
972         pragma(inline, false);
973         FloatingPointControl fpctrl;
974         fpctrl.rounding = rm;
975         T x = -1;
976         x = forceSubOp(x, 0.1L);
977         return x;
978     }
979 
980     static foreach (T; AliasSeq!(float, double, real))
981     {{
982         /* Be careful with changing the rounding mode, it interferes
983          * with common subexpressions. Changing rounding modes should
984          * be done with separate functions that are not inlined.
985          */
986 
987         {
988             T u = addRound!(T)(FloatingPointControl.roundUp);
989             T d = addRound!(T)(FloatingPointControl.roundDown);
990             T z = addRound!(T)(FloatingPointControl.roundToZero);
991 
992             assert(u > d);
993             assert(z == d);
994         }
995 
996         {
997             T u = subRound!(T)(FloatingPointControl.roundUp);
998             T d = subRound!(T)(FloatingPointControl.roundDown);
999             T z = subRound!(T)(FloatingPointControl.roundToZero);
1000 
1001             assert(u > d);
1002             assert(z == u);
1003         }
1004     }}
1005 }
1006 
1007 } // FloatingPointControlSupport
1008 
1009 version (StdUnittest)
1010 {
1011     // These helpers are intended to avoid constant propagation by the optimizer.
1012     pragma(inline, false) private @safe
1013     {
1014         T forceAddOp(T)(T x, T y) { return x + y; }
1015         T forceSubOp(T)(T x, T y) { return x - y; }
1016         T forceMulOp(T)(T x, T y) { return x * y; }
1017         T forceDivOp(T)(T x, T y) { return x / y; }
1018     }
1019 }